package com.huahua.api.service.impl;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.huahua.api.entity.param.TableComparisonConfig;
import com.huahua.api.entity.param.TableComparisonParam;
import com.huahua.api.entity.vo.tablecomparison.*;
import com.huahua.api.enums.ColumnMatchMode;
import com.huahua.api.enums.RowStatus;
import com.huahua.api.exception.CommonException;
import com.huahua.api.service.TableComparisonService;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.poi.excel.ExcelUtil;
import org.dromara.hutool.poi.excel.reader.ExcelReader;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TableComparisonServiceImpl implements TableComparisonService {

    private final Cache<String, TableComparisonVO> resultCache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .build();

    private final Map<String, byte[]> downloadCache = new ConcurrentHashMap<>();

    @Override
    public Mono<TableComparisonVO> compare(TableComparisonParam param) {
        return Mono.fromCallable(() -> {
            TableComparisonConfig config = param.getConfig();
            if (config == null) {
                config = new TableComparisonConfig();
            }

            // 下载文件
            List<List<Object>> table1Data = downloadAndParse(param.getTable1Url());
            List<List<Object>> table2Data = downloadAndParse(param.getTable2Url());

            // 构建对比结果
            TableComparisonVO result = buildComparisonResult(
                    table1Data, table2Data,
                    getFileNameFromUrl(param.getTable1Url()),
                    getFileNameFromUrl(param.getTable2Url()),
                    config
            );

            // 缓存结果
            resultCache.put(result.getComparisonId(), result);

            return result;
        }).subscribeOn(Schedulers.boundedElastic());
    }

    @Override
    public Mono<Flux<DataBuffer>> downloadResult(String comparisonId) {
        return Mono.fromCallable(() -> {
            TableComparisonVO result = resultCache.getIfPresent(comparisonId);
            if (result == null) {
                return null;
            }

            // 检查缓存
            byte[] cached = downloadCache.get(comparisonId);
            if (cached != null) {
                return Flux.<DataBuffer>just(DefaultDataBufferFactory.sharedInstance.wrap(cached));
            }

            // 生成CSV格式的对比结果（暂时替代Excel）
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));

            // 写入摘要信息
            writer.println("表格对比结果");
            writer.println();
            writer.println("文件1：" + result.getSummary().getTable1().getFileName());
            writer.println("文件2：" + result.getSummary().getTable2().getFileName());
            writer.println("相同行数：" + result.getSummary().getComparison().getSameRows());
            writer.println("修改行数：" + result.getSummary().getComparison().getModifiedRows());
            writer.println("新增行数：" + result.getSummary().getComparison().getAddedRows());
            writer.println("删除行数：" + result.getSummary().getComparison().getDeletedRows());
            writer.println();

            // 写入详细对比数据
            writer.println("行号,状态,差异详情");
            for (RowComparison row : result.getDetails().getRows()) {
                String diffStr = "";
                if (!row.getDifferences().isEmpty()) {
                    diffStr = row.getDifferences().stream()
                            .map(diff -> diff.getColumnName() + ": " + diff.getTable1Value() + " → " + diff.getTable2Value())
                            .collect(Collectors.joining("; "));
                }
                writer.println(row.getRowNumber() + "," + row.getStatus().getDescription() + "," + diffStr);
            }

            writer.flush();
            writer.close();

            byte[] bytes = out.toByteArray();
            downloadCache.put(comparisonId, bytes);

            return Flux.<DataBuffer>just(DefaultDataBufferFactory.sharedInstance.wrap(bytes));
        }).subscribeOn(Schedulers.boundedElastic());
    }

    private final WebClient webClient = WebClient.builder()
            .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024))
            .build();

    private List<List<Object>> downloadAndParse(String url) {
        try {
            // 从URL获取文件名
            String fileName = getFileNameFromUrl(url).toLowerCase();

            // 下载文件到临时位置
            Path tempFile = Files.createTempFile("table_", getFileExtension(fileName));

            byte[] fileBytes = webClient.get()
                    .uri(url)
                    .retrieve()
                    .bodyToMono(byte[].class)
                    .block();

            Files.write(tempFile, fileBytes);

            List<List<Object>> data;

            if (fileName.endsWith(".csv")) {
                // CSV文件解析
                data = parseCsvFile(tempFile);
            } else if (fileName.endsWith(".xlsx") || fileName.endsWith(".xls")) {
                // 解析Excel文件
                data = parseExcelFile(tempFile);
            } else {
                throw new CommonException("不支持的文件格式：" + fileName);
            }

            Files.deleteIfExists(tempFile);
            return data;
        } catch (Exception e) {
            throw new CommonException("文件下载或解析失败：" + e.getMessage());
        }
    }

    private String getFileNameFromUrl(String url) {
        String[] parts = url.split("/");
        return parts[parts.length - 1].split("\\?")[0];
    }

    private String getFileExtension(String fileName) {
        int lastDot = fileName.lastIndexOf('.');
        return lastDot > 0 ? fileName.substring(lastDot) : "";
    }

    private List<List<Object>> parseCsvFile(Path file) throws IOException {
        List<List<Object>> data = new ArrayList<>();
        try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                List<Object> row = Arrays.asList(line.split(",", -1));
                data.add(row);
            }
        }
        return data;
    }

    private List<List<Object>> parseExcelFile(Path file) throws IOException {
        List<List<Object>> data = new ArrayList<>();

        try {
            // 获取getReader方法
            ExcelReader reader = ExcelUtil.getReader(file.toFile());
            return reader.read();
        } catch (Exception e) {
            log.error("Excel文件解析失败", e);
            throw new CommonException("Excel文件解析失败：" + e.getMessage());
        }
    }

    private TableComparisonVO buildComparisonResult(
            List<List<Object>> table1Data,
            List<List<Object>> table2Data,
            String fileName1,
            String fileName2,
            TableComparisonConfig config) {

        // 提取表头
        List<String> headers1 = extractHeaders(table1Data);
        List<String> headers2 = extractHeaders(table2Data);

        // 构建列映射
        List<ColumnMapping> columnMappings = buildColumnMappings(headers1, headers2, config);

        // 对比数据行
        List<RowComparison> rowComparisons = compareRows(table1Data, table2Data, columnMappings, config);

        // 统计信息
        ComparisonStatistics statistics = calculateStatistics(rowComparisons);

        // 构建结果
        return TableComparisonVO.builder()
                .comparisonId("comp_" + UUID.randomUUID().toString().replace("-", ""))
                .summary(ComparisonSummary.builder()
                        .table1(TableInfo.builder()
                                .totalRows(table1Data.size())
                                .totalColumns(headers1.size())
                                .fileName(fileName1)
                                .build())
                        .table2(TableInfo.builder()
                                .totalRows(table2Data.size())
                                .totalColumns(headers2.size())
                                .fileName(fileName2)
                                .build())
                        .comparison(statistics)
                        .build())
                .details(ComparisonDetails.builder()
                        .columns(columnMappings)
                        .rows(rowComparisons)
                        .build())
                .build();
    }

    private List<String> extractHeaders(List<List<Object>> tableData) {
        if (tableData.isEmpty()) {
            return new ArrayList<>();
        }
        return tableData.get(0).stream()
                .map(Object::toString)
                .collect(Collectors.toList());
    }

    private List<ColumnMapping> buildColumnMappings(
            List<String> headers1,
            List<String> headers2,
            TableComparisonConfig config) {

        List<ColumnMapping> mappings = new ArrayList<>();

        if (config.getColumnMatchMode() == ColumnMatchMode.BY_NAME) {
            // 按列名匹配
            Map<String, Integer> header2Map = new HashMap<>();
            for (int i = 0; i < headers2.size(); i++) {
                header2Map.put(processColumnName(headers2.get(i), config), i);
            }

            for (int i = 0; i < headers1.size(); i++) {
                String name1 = headers1.get(i);
                String processedName1 = processColumnName(name1, config);
                Integer matchIndex = header2Map.get(processedName1);

                if (matchIndex != null) {
                    mappings.add(ColumnMapping.builder()
                            .index(i)
                            .table1Name(name1)
                            .table2Name(headers2.get(matchIndex))
                            .matched(true)
                            .build());
                } else {
                    mappings.add(ColumnMapping.builder()
                            .index(i)
                            .table1Name(name1)
                            .table2Name(null)
                            .matched(false)
                            .build());
                }
            }
        } else {
            // 按索引匹配
            int maxColumns = Math.max(headers1.size(), headers2.size());
            for (int i = 0; i < maxColumns; i++) {
                mappings.add(ColumnMapping.builder()
                        .index(i)
                        .table1Name(i < headers1.size() ? headers1.get(i) : null)
                        .table2Name(i < headers2.size() ? headers2.get(i) : null)
                        .matched(i < headers1.size() && i < headers2.size())
                        .build());
            }
        }

        return mappings;
    }

    private String processColumnName(String name, TableComparisonConfig config) {
        if (config.getIgnoreCase()) {
            name = name.toLowerCase();
        }
        if (config.getIgnoreSpace()) {
            name = name.trim();
        }
        return name;
    }

    private List<RowComparison> compareRows(
            List<List<Object>> table1Data,
            List<List<Object>> table2Data,
            List<ColumnMapping> columnMappings,
            TableComparisonConfig config) {

        List<RowComparison> comparisons = new ArrayList<>();

        // 跳过表头行
        List<List<Object>> data1 = table1Data.size() > 1 ? table1Data.subList(1, table1Data.size()) : new ArrayList<>();
        List<List<Object>> data2 = table2Data.size() > 1 ? table2Data.subList(1, table2Data.size()) : new ArrayList<>();

        if (!config.getIgnoreRowOrder()) {
            // 按行号顺序对比
            int maxRows = Math.max(data1.size(), data2.size());

            for (int i = 0; i < maxRows; i++) {
                List<Object> row1 = i < data1.size() ? data1.get(i) : null;
                List<Object> row2 = i < data2.size() ? data2.get(i) : null;

                if (row1 == null) {
                    // 新增行
                    comparisons.add(RowComparison.builder()
                            .rowNumber(i + 2) // 加2是因为跳过了表头，且行号从1开始
                            .status(RowStatus.ADDED)
                            .table1Data(null)
                            .table2Data(convertRowToStringList(row2))
                            .differences(new ArrayList<>())
                            .build());
                } else if (row2 == null) {
                    // 删除行
                    comparisons.add(RowComparison.builder()
                            .rowNumber(i + 2)
                            .status(RowStatus.DELETED)
                            .table1Data(convertRowToStringList(row1))
                            .table2Data(null)
                            .differences(new ArrayList<>())
                            .build());
                } else {
                    // 对比行内容
                    List<CellDifference> differences = compareRowContent(row1, row2, columnMappings, config);
                    comparisons.add(RowComparison.builder()
                            .rowNumber(i + 2)
                            .status(differences.isEmpty() ? RowStatus.SAME : RowStatus.MODIFIED)
                            .table1Data(convertRowToStringList(row1))
                            .table2Data(convertRowToStringList(row2))
                            .differences(differences)
                            .build());
                }
            }
        } else {
            // 忽略行顺序，基于内容进行匹配
            return compareRowsIgnoringOrder(table1Data, table2Data, columnMappings, config);
        }

        return comparisons;
    }

    private List<CellDifference> compareRowContent(
            List<Object> row1,
            List<Object> row2,
            List<ColumnMapping> columnMappings,
            TableComparisonConfig config) {

        List<CellDifference> differences = new ArrayList<>();

        for (ColumnMapping mapping : columnMappings) {
            if (!mapping.getMatched()) {
                continue;
            }

            int index = mapping.getIndex();
            String value1 = index < row1.size() ? processValue(row1.get(index), config) : "";
            String value2 = index < row2.size() ? processValue(row2.get(index), config) : "";

            if (!value1.equals(value2)) {
                differences.add(CellDifference.builder()
                        .columnIndex(index)
                        .columnName(mapping.getTable1Name())
                        .table1Value(value1)
                        .table2Value(value2)
                        .build());
            }
        }

        return differences;
    }

    private String processValue(Object value, TableComparisonConfig config) {
        String str = value == null ? "" : value.toString();
        if (config.getIgnoreCase()) {
            str = str.toLowerCase();
        }
        if (config.getIgnoreSpace()) {
            str = str.trim();
        }
        return str;
    }

    private List<String> convertRowToStringList(List<Object> row) {
        return row.stream()
                .map(obj -> obj == null ? "" : obj.toString())
                .collect(Collectors.toList());
    }

    private List<RowComparison> compareRowsIgnoringOrder(
            List<List<Object>> table1Data,
            List<List<Object>> table2Data,
            List<ColumnMapping> columnMappings,
            TableComparisonConfig config) {

        List<RowComparison> comparisons = new ArrayList<>();

        // 跳过表头行
        List<List<Object>> data1 = table1Data.size() > 1 ? table1Data.subList(1, table1Data.size()) : new ArrayList<>();
        List<List<Object>> data2 = table2Data.size() > 1 ? table2Data.subList(1, table2Data.size()) : new ArrayList<>();

        // 构建内容到行号的映射，处理重复行
        Map<String, List<Integer>> table1Map = buildRowContentMap(data1, columnMappings, config);
        Map<String, List<Integer>> table2Map = buildRowContentMap(data2, columnMappings, config);

        // 用于跟踪已处理的行
        Set<Integer> processedRows1 = new HashSet<>();
        Set<Integer> processedRows2 = new HashSet<>();

        // 处理表1中的每一行
        for (Map.Entry<String, List<Integer>> entry : table1Map.entrySet()) {
            String rowKey = entry.getKey();
            List<Integer> rows1 = entry.getValue();
            List<Integer> rows2 = table2Map.getOrDefault(rowKey, new ArrayList<>());

            int count1 = rows1.size();
            int count2 = rows2.size();

            // 处理相同的行
            int sameCount = Math.min(count1, count2);
            for (int i = 0; i < sameCount; i++) {
                int rowIndex1 = rows1.get(i);
                int rowIndex2 = rows2.get(i);

                comparisons.add(RowComparison.builder()
                        .rowNumber(rowIndex1 + 2) // +2 因为跳过了表头且行号从1开始
                        .status(RowStatus.SAME)
                        .table1Data(convertRowToStringList(data1.get(rowIndex1)))
                        .table2Data(convertRowToStringList(data2.get(rowIndex2)))
                        .differences(new ArrayList<>())
                        .build());

                processedRows1.add(rowIndex1);
                processedRows2.add(rowIndex2);
            }

            // 处理表1中多出的行（删除）
            if (count1 > count2) {
                for (int i = sameCount; i < count1; i++) {
                    int rowIndex = rows1.get(i);
                    comparisons.add(RowComparison.builder()
                            .rowNumber(rowIndex + 2)
                            .status(RowStatus.DELETED)
                            .table1Data(convertRowToStringList(data1.get(rowIndex)))
                            .table2Data(null)
                            .differences(new ArrayList<>())
                            .build());
                    processedRows1.add(rowIndex);
                }
            }
        }

        // 处理表2中独有的行（新增）
        for (Map.Entry<String, List<Integer>> entry : table2Map.entrySet()) {
            String rowKey = entry.getKey();
            if (!table1Map.containsKey(rowKey)) {
                List<Integer> rows2 = entry.getValue();
                for (Integer rowIndex : rows2) {
                    comparisons.add(RowComparison.builder()
                            .rowNumber(rowIndex + 2)
                            .status(RowStatus.ADDED)
                            .table1Data(null)
                            .table2Data(convertRowToStringList(data2.get(rowIndex)))
                            .differences(new ArrayList<>())
                            .build());
                    processedRows2.add(rowIndex);
                }
            } else {
                // 处理表2中多出的重复行
                List<Integer> rows1 = table1Map.get(rowKey);
                List<Integer> rows2 = entry.getValue();
                int count1 = rows1.size();
                int count2 = rows2.size();

                if (count2 > count1) {
                    for (int i = count1; i < count2; i++) {
                        int rowIndex = rows2.get(i);
                        if (!processedRows2.contains(rowIndex)) {
                            comparisons.add(RowComparison.builder()
                                    .rowNumber(rowIndex + 2)
                                    .status(RowStatus.ADDED)
                                    .table1Data(null)
                                    .table2Data(convertRowToStringList(data2.get(rowIndex)))
                                    .differences(new ArrayList<>())
                                    .build());
                            processedRows2.add(rowIndex);
                        }
                    }
                }
            }
        }

        // 按行号排序，便于阅读
        comparisons.sort(Comparator.comparing(RowComparison::getRowNumber));

        return comparisons;
    }

    private Map<String, List<Integer>> buildRowContentMap(
            List<List<Object>> data,
            List<ColumnMapping> columnMappings,
            TableComparisonConfig config) {

        Map<String, List<Integer>> contentMap = new LinkedHashMap<>();

        for (int i = 0; i < data.size(); i++) {
            List<Object> row = data.get(i);
            String rowKey = generateRowKey(row, columnMappings, config);

            contentMap.computeIfAbsent(rowKey, k -> new ArrayList<>()).add(i);
        }

        return contentMap;
    }

    private String generateRowKey(List<Object> row, List<ColumnMapping> columnMappings, TableComparisonConfig config) {
        StringBuilder keyBuilder = new StringBuilder();

        for (int i = 0; i < row.size() && i < columnMappings.size(); i++) {
            if (columnMappings.get(i).getMatched()) {
                String value = processValue(row.get(i), config);
                keyBuilder.append(value).append("|");
            }
        }

        return keyBuilder.toString();
    }

    private ComparisonStatistics calculateStatistics(List<RowComparison> comparisons) {
        int sameRows = 0;
        int modifiedRows = 0;
        int addedRows = 0;
        int deletedRows = 0;

        for (RowComparison comparison : comparisons) {
            switch (comparison.getStatus()) {
                case SAME:
                    sameRows++;
                    break;
                case MODIFIED:
                    modifiedRows++;
                    break;
                case ADDED:
                    addedRows++;
                    break;
                case DELETED:
                    deletedRows++;
                    break;
            }
        }

        return ComparisonStatistics.builder()
                .sameRows(sameRows)
                .modifiedRows(modifiedRows)
                .addedRows(addedRows)
                .deletedRows(deletedRows)
                .totalDifferences(modifiedRows + addedRows + deletedRows)
                .build();
    }
}
